/**
 * 
 */
package ar.utn.frgp.banco.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import ar.utn.frgp.banco.dao.CuentasDAO;
import ar.utn.frgp.banco.entity.Cliente;
import ar.utn.frgp.banco.entity.Cuenta;
import ar.utn.frgp.banco.entity.Movimientos;
import ar.utn.frgp.banco.entity.enums.OrigenMovimiento;
import ar.utn.frgp.banco.entity.enums.TipoCuenta;
import ar.utn.frgp.banco.entity.enums.TipoMovimiento;
import ar.utn.frgp.banco.exceptions.DataAccessException;
import ar.utn.frgp.banco.exceptions.PersonalisedException;
import ar.utn.frgp.banco.exceptions.ServiceException;
import ar.utn.frgp.banco.service.EBAbstractService;
import ar.utn.frgp.banco.service.GestionCuentasService;

/**
 * @author PUSDERKIS
 *
 */
public class GestionCuentasServiceImpl extends EBAbstractService implements GestionCuentasService{
	
	private CuentasDAO cuentasDAO;

	public List<Cuenta> obteneCuentas(String dni) throws ServiceException,
			PersonalisedException {
		if (dni == null || "".equals(dni)){
			logger.error("No se proporciono un dni valido para recuperar la lista de cuentas.");
			throw new ServiceException("No se proporciono un dni valido para recuperar la lista de cuentas.");
		}
		
		return cuentasDAO.obtenerCuentas(dni);
	}

	/**
	 * @see ar.utn.frgp.banco.service.GestionCuentasService#cantidadCuentasCorrientes(java.lang.String)
	 */
	public int cantidadCuentasCorrientes(String dni) throws ServiceException,
			PersonalisedException {
		try{
			int cant = cuentasDAO.cantidadCuentas(dni,TipoCuenta.CUENTA_CORRIENTE);
			return cant;
		} catch (DataAccessException e) {
			logger.error("Fallo al obtener la cantidad de cuentas corrientes",e);
			throw new PersonalisedException("Fallo al obtener la cantidad de cuentas corrientes.", e);
		}
	}
	
	/**
	 * @see ar.utn.frgp.banco.service.GestionCuentasService#cantidadCajaAhorro(java.lang.String)
	 */
	public int cantidadCajaAhorro(String dni) throws ServiceException,
			PersonalisedException {
		try{
			int cant = cuentasDAO.cantidadCuentas(dni,TipoCuenta.CAJA_AHORROS);
			return cant;
		} catch (DataAccessException e) {
			logger.error("Fallo al obtener la cantidad de cuentas corrientes",e);
			throw new PersonalisedException("Fallo al obtener la cantidad de cuentas corrientes.", e);
		}
	}

	public List<Cuenta> obtenerCuentasCorrientes(String dni)
			throws ServiceException, PersonalisedException {
		// TODO Auto-generated method stub
		return null;
	}

	public Cuenta obtenerCajaAhorro(String dni) throws ServiceException,
			PersonalisedException {
		return null;
	}

	public int tieneCajaDeAhorro(String dni) throws ServiceException,
			PersonalisedException {
		// TODO Auto-generated method stub
		return 0;
	}

	public Cuenta crearCuenta(Cuenta cta, String dni) throws ServiceException,
			PersonalisedException {
		if (cta == null){
			logger.error("No se indico la cuenta que se desea crear");
			throw new ServiceException("No se indico la cuenta que se desea crear");
		}
		if (dni == null || "".equals(dni)){
			logger.error("No se indico un dni valido");
			throw new ServiceException("No se indico un dni valido");
		}
		if (TipoCuenta.CUENTA_CORRIENTE.equals(cta.getTipoCuenta())){
			if (cta.getTipoCuenta().getMaximo() <= cantidadCuentasCorrientes(dni)){
				logger.error("Se supero el maximo de cuentas corrientes para el cliente con dni: "+dni);
				throw new ServiceException("Se supero el maximo de cuentas corrientes para el cliente con dni: "+dni);
			}
		} else {
			if (cta.getTipoCuenta().getMaximo() <= cantidadCajaAhorro(dni)){
				logger.error("El cliente con dni " + dni + " ya tiene una caja de ahorro.");
				throw new ServiceException("El cliente con dni " + dni + " ya tiene una caja de ahorro.");
			}
		}
		
		Cliente cliente = commonDAO.load(Cliente.class, dni);
		
		cliente.getCuentas().add(cta); //agrega el objeto cuenta al listado de cuentas del cliente
		commonDAO.save(cliente);

		return cta;
	}

	public Cuenta obtenerCuentaCompleta(Long nroCuenta) throws ServiceException,
			PersonalisedException {
		
		Cuenta cuenta = cuentasDAO.obtenerCuenta(nroCuenta);
		
		return cuenta;
	}


	public Cuenta deposito(Long nroCuenta, BigDecimal monto)
			throws ServiceException, PersonalisedException {

		Cuenta cuenta = commonDAO.load(Cuenta.class, nroCuenta);
		
		try {
			cuenta.setSaldo(cuenta.getSaldo().add(monto));
			
			Movimientos mov = new Movimientos();
			
			mov.setFecha(new Date());
			mov.setMontoMovimiento(monto);
			mov.setOrigen(OrigenMovimiento.CAJA);
			mov.setTipoMov(TipoMovimiento.DEPOSITO);
			mov.setSaldoActual(cuenta.getSaldo());
			
			cuenta.getMovimientos().add(mov);
			commonDAO.save(cuenta);
			
		} catch (Exception e) {
			// TODO: handle exception
		}
		
		return cuenta;
		
	}


	public Cuenta extraccion(Long nroCuenta, BigDecimal monto)
			throws ServiceException, PersonalisedException {
		// TODO Auto-generated method stub


		Cuenta cuenta = commonDAO.load(Cuenta.class, nroCuenta);
		
		//Cuenta cuenta = cuentasDAO.obtenerCuenta(nroCuenta);
		
		if(cuenta.getSaldo().doubleValue() + cuenta.getDescubierto().doubleValue() - monto.doubleValue() < 0){
			logger.error("No dispone del saldo suficiente");
			throw new PersonalisedException("No dispone del saldo suficiente");
		}
		
		try {
			cuenta.setSaldo(cuenta.getSaldo().subtract(monto));
			
			Movimientos mov = new Movimientos();
			
			mov.setFecha(new Date());
			mov.setMontoMovimiento(monto);
			mov.setOrigen(OrigenMovimiento.CAJA);
			mov.setTipoMov(TipoMovimiento.EXTRACCION);
			mov.setSaldoActual(cuenta.getSaldo());
			
			cuenta.getMovimientos().add(mov);
			commonDAO.save(cuenta);
			
		} catch (Exception e) {
			// TODO: handle exception
		}
		
		return cuenta;
	}

	public void transferencia(Long nroCuenta, BigDecimal monto,
			Long nroCuentaDestino) throws ServiceException,
			PersonalisedException {
		// TODO Auto-generated method stub
		
		Cuenta cuenta = cuentasDAO.obtenerCuenta(nroCuenta);
		Cuenta cuenta2 = cuentasDAO.obtenerCuenta(nroCuentaDestino);
		
		if(cuenta.getSaldo().doubleValue() + cuenta.getDescubierto().doubleValue() - monto.doubleValue() < 0){
			logger.error("No dispone del saldo suficiente");
			throw new PersonalisedException("No dispone del saldo suficiente");
		}
		
		try {
			cuenta.setSaldo(cuenta.getSaldo().subtract(monto));
			
			Movimientos mov = new Movimientos();
			
			mov.setFecha(new Date());
			mov.setMontoMovimiento(monto);
			mov.setOrigen(OrigenMovimiento.TRANSFERENCIA);
			mov.setTipoMov(TipoMovimiento.EXTRACCION);
			mov.setSaldoActual(cuenta.getSaldo());
			
			cuenta.getMovimientos().add(mov);
			commonDAO.save(cuenta);
			
			cuenta2.setSaldo(cuenta2.getSaldo().add(monto));
			
			Movimientos mov2 = new Movimientos();
			
			mov2.setFecha(new Date());
			mov2.setMontoMovimiento(monto);
			mov2.setOrigen(OrigenMovimiento.TRANSFERENCIA);
			mov2.setTipoMov(TipoMovimiento.DEPOSITO);
			mov2.setSaldoActual(cuenta2.getSaldo());
			
			cuenta2.getMovimientos().add(mov2);
			commonDAO.save(cuenta2);
			
		} catch (Exception e) {
			// TODO: handle exception
		}
		
	}

	/**
	 * @param cuentasDAO the cuentasDAO to set
	 */
	public void setCuentasDAO(CuentasDAO cuentasDAO) {
		this.cuentasDAO = cuentasDAO;
	}
}
